﻿using Aribaccio.Device.Modbus;
using Aribaccio.Device.Struct;
using Aribaccio.Logger;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Runtime.InteropServices.JavaScript.JSType;

namespace Aribaccio.Device
{
    /// <summary>
    /// Modbusl类型设备串口基类
    /// </summary>
    /// <typeparam name="TPara">设备串行连接参数接口</typeparam>
    public class DeviceSerialModbus<TPara> : DeviceSerial<TPara> where TPara : IDevicePara
    {
        /// <summary>
        /// 要检测冗余调用
        /// </summary>
        protected bool DisposedValue { get; set; }
        /// <summary>
        /// 是否在发送前清除缓冲
        /// </summary>
        protected bool IsClearCacheBeforeRead { get; set; }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="argDevicePara">设备参数接口</param>
        public DeviceSerialModbus(TPara argDevicePara)
            : base(argDevicePara)
        {
            if (!this.IsAutoReceivedMode)
            {
                this.SleepTime = 20;
            }
            this.NodeDataStartNum = 2;
            this.DisposedValue = false;
            this.IsClearCacheBeforeRead = false;
        }

        /// <summary>
        /// 发送Modbus串口数据
        /// </summary>
        /// <typeparam name="TInput">输入结构类型</typeparam>
        /// <param name="argNodeItem">Modbus指令节点数据对象</param>
        /// <returns>返回结果对象</returns>
        public Result WriteModbusData<TInput>(ModbusSlaveData<TInput> argNodeItem) where TInput : struct
        {
            Result ResultData = new Result(ResultStatus.Failure);
            if (argNodeItem == null) { return ResultData; }
            try
            {
                ////不需要进一步校验，直接赋值
                //ResultData = this.SlaveSerialData(argNodeItem, false);
                //如果需要进一步校验
                ResultModel<byte[]> ResultObjItem = this.SlaveSerialData(argNodeItem, argNodeItem.SlaveData, true);
                //判断是否成功
                if (ResultObjItem.ResultState && ResultObjItem.Data != null && ResultObjItem.Data.Length >= base.NodeDataNum)
                {
                    byte ReslutCheckSum = ResultObjItem.Data[ResultObjItem.Data.Length - 2];
                    byte ReslutStopBit = ResultObjItem.Data[ResultObjItem.Data.Length - 1];
                    //截取新数组[去除后两位校验字节]
                    byte[] Bytes = new byte[ResultObjItem.Data.Length - 2];
                    Array.ConstrainedCopy(ResultObjItem.Data, 0, Bytes, 0, (ResultObjItem.Data.Length - 2));
                    //根据校验类型，判断校验
                    switch (argNodeItem.CheckType)
                    {
                        case DeviceCheck.CRC:
                            byte[] CRCCheckBytes = Bytes.ToCheckCRCModbus();
                            if (CRCCheckBytes[0] == ReslutCheckSum && CRCCheckBytes[1] == ReslutStopBit)
                            {
                                ResultData.ResultCode = ResultStatus.Succeed;
                            }
                            break;
                    }
                    ResultData.ResultMsg = ResultData.ResultCode == ResultStatus.Succeed ? string.Empty : "发送校验失败";
                }
                else
                {
                    ResultData.ResultCode = !ResultObjItem.ResultState ? ResultObjItem.ResultCode : ResultStatus.Failure;
                    ResultData.ResultMsg = !ResultObjItem.ResultState ? ResultObjItem.ResultMsg : "[" + base.DevicePara.DeviceCaption + "][读取Modbus串口数据]->发送数据失败";
                }
            }
            catch (Exception ep)
            {
                ResultData.ResultCode = ResultStatus.Exception;
                ResultData.ResultMsg = "[" + base.DevicePara.DeviceCaption + "][发送Modbus串口数据]->异常：" + ep.Message;
            }
            return ResultData;
        }

        /// <summary>
        /// 读取Modbus串口数据
        /// </summary>
        /// <typeparam name="TInput">输入结构类型</typeparam>
        /// <param name="argNodeItem">Modbus指令节点数据对象</param>
        /// <returns>返回接收字节的结果对象</returns>
        public ResultModel<byte[]> RreadModbusByteData<TInput>(ModbusSlaveData<TInput> argNodeItem) where TInput : struct
        {
            ResultModel<byte[]> ResultData = new ResultModel<byte[]>() { ResultCode = ResultStatus.Failure };
            if (argNodeItem == null) { return ResultData; }
            try
            {
                //解析，读取数据
                ResultData = this.SlaveSerialData(argNodeItem, argNodeItem.SlaveData, true);
            }
            catch (Exception ep)
            {
                ResultData.ResultCode = ResultStatus.Exception;
                ResultData.ResultMsg = "[" + base.DevicePara.DeviceCaption + "][读取Modbus串口数据]->异常：" + ep.Message;
            }
            return ResultData;
        }

        /// <summary>
        /// 读取Modbus串口数据
        /// </summary>
        /// <typeparam name="TInput">输入结构类型</typeparam>
        /// <typeparam name="TOutput">输出结构类型</typeparam>
        /// <param name="argNodeItem">Modbus指令节点数据对象</param>
        /// <returns>返回接收Modbus指令节点数据的结果对象 </returns>
        public ResultModel<ModbusSlaveData<TOutput>> ReadModbusModelData<TInput, TOutput>(ModbusSlaveData<TInput> argNodeItem)
            where TInput : struct
            where TOutput : struct
        {
            ResultModel<ModbusSlaveData<TOutput>> ResultData = new ResultModel<ModbusSlaveData<TOutput>>(ResultStatus.Failure) { Data = new ModbusSlaveData<TOutput>(argNodeItem) };
            if (argNodeItem == null) { return ResultData; }
            try
            {
                //解析，读取数据
                ResultModel<byte[]> ResultObj = this.SlaveSerialData<TInput, TOutput>(argNodeItem, ResultData.Data.SlaveData, true);
                //判断是否读取成功
                if (ResultObj.ResultState && ResultObj.Data != null && ResultObj.Data.Length >= base.NodeDataNum)
                {
                    var result = ParseDeviceData<TOutput>(ResultObj.Data);
                    ResultData.Data = result.Data;
                    ResultData.ResultCode = result.ResultCode;
                    ResultData.ResultMsg = result.ResultMsg;
                }
                else
                {
                    ResultData.Data.SlaveData = default;
                    ResultData.ResultCode = ResultStatus.Failure;
                    ResultData.ResultMsg = !ResultObj.ResultState ? ResultObj.ResultMsg : "[" + base.DevicePara.DeviceCaption + "][读取Modbus串口数据]->读取数据失败";
                }
            }
            catch (Exception ep)
            {
                ResultData.ResultCode = ResultStatus.Exception;
                ResultData.ResultMsg = "[" + base.DevicePara.DeviceCaption + "][读取Modbus串口数据]->异常：" + ep.Message;
            }
            return ResultData;
        }

        /// <summary>
        /// 读取串口的数据
        /// </summary>
        /// <typeparam name="TInput">输入结构类型</typeparam>
        /// <typeparam name="TOutput">输出结构类型</typeparam>
        /// <param name="argInputNodeItem">发送Modbus指令节点数据对象</param>
        /// <param name="argOutputItem">接收Modbus数据对象</param>
        /// <param name="IsWaitRaed">是否读取等待</param>
        /// <returns>返回接收字节的结果对象</returns>
        protected ResultModel<byte[]> SlaveSerialData<TInput, TOutput>(ModbusSlaveData<TInput> argInputNodeItem, TOutput argOutputItem, bool IsWaitRaed)
            where TInput : struct
            where TOutput : struct
        {
            //获取锁
            base.HybirdLock.Enter();
            ResultModel<byte[]> ResultData = new ResultModel<byte[]>() { ResultCode = ResultStatus.Failure };
            //解析节点数据
            ResultModel<byte[]> ResultObjData = this.ParseNodeData(argInputNodeItem);
            if (this.DevicePara.DeviceType == "Electrical")
            {
                LoggerMgrcore.Instance.WriteDebug($"{this.DevicePara.DeviceCaption} SlaveSerialData  => 开始 {argInputNodeItem.ToStringHex()}");
            }
            if (ResultObjData.ResultState && ResultObjData.Data != null && ResultObjData.Data.Length > 0)
            {
                //判断是否需要清除缓存
                if (this.IsClearCacheBeforeRead) { base.RreadSerialPortData(false); }
                if (IsWaitRaed)
                {
                    for (int i = 0; i < base.ReceiveTimeoutNum; i++)
                    {
                        //发送串口指令
                        base.SendSerialPortData(ResultObjData.Data);
                        if (this.DevicePara.DeviceType == "Electrical")
                        {
                            LoggerMgrcore.Instance.WriteDebug($"{this.DevicePara.DeviceCaption} SlaveSerialData  SendSerialPortData IsWaitRaed => 成功");
                        }
                        //读取数据
                        ResultData = base.RreadSerialPortData(true);
                        //判断是否读取成功
                        if (ResultData.ResultState && ResultData.Data != null)
                        {
                            if (this.DevicePara.DeviceType == "Electrical")
                            {
                                LoggerMgrcore.Instance.WriteDebug($"{this.DevicePara.DeviceCaption} SlaveSerialData  RreadSerialPortData IsWaitRaed => 成功");
                            }
                            break;                           
                        }
                        else
                        {
                            LoggerMgrcore.Instance.WriteDebug($"{this.DevicePara.DeviceName} SlaveSerialData RreadSerialPortData[{i}] error ：{ResultData.ResultMsg}");
                        }
                    }
                }
                else
                {
                    Result SendResult = base.SendSerialPortData(ResultObjData.Data);
                    if (this.DevicePara.DeviceType == "Electrical")
                    {
                        LoggerMgrcore.Instance.WriteDebug($"{this.DevicePara.DeviceCaption} SlaveSerialData  SendSerialPortData => 成功");
                    }
                    ResultData.ResultCode = SendResult.ResultCode;
                    ResultData.ResultMsg = SendResult.ResultMsg;
                }
                //离开锁
                base.HybirdLock.Leave();
            }
            else
            {
                //离开锁
                base.HybirdLock.Leave();
                ResultData = ResultObjData;
            }
            return ResultData;
        }

        /// <summary>
        /// 解析Modbus指令数据
        /// </summary>
        /// <typeparam name="TInput">输入结构类型</typeparam>
        /// <param name="argNodeItem">Modbus指令节点数据对象</param>
        /// <returns>返回接收字节的结果对象</returns>
        protected ResultModel<byte[]> ParseNodeData<TInput>(ModbusSlaveData<TInput> argNodeItem) where TInput : struct
        {
            lock (argNodeItem)
            {
                ResultModel<byte[]> ResultData = new ResultModel<byte[]>() { ResultCode = ResultStatus.Failure };
                if (argNodeItem == null) { return ResultData; }
                try
                {
                    //数据体
                    byte[] NodeData = argNodeItem.SlaveData.StructToBytesEndian<TInput>();
                    //寄存器地址
                    byte[] rgtAddressData = ((ushort)argNodeItem.RegisterAddress).StructToBytes();
                    //定义字节数组
                    int nLengthtAddress = (rgtAddressData != null && rgtAddressData.Length > 0 ? rgtAddressData.Length : 0);
                    int nLengthtData = (NodeData != null && NodeData.Length > 0 ? NodeData.Length : 0);
                    byte[] Data = new byte[base.NodeDataNum + nLengthtAddress + nLengthtData];
                    //起始或头
                    Data[0] = (byte)argNodeItem.SlaveAddress;
                    //功能码
                    Data[1] = (byte)argNodeItem.FunctionCode;
                    //添加寄存器地址
                    if (rgtAddressData != null && rgtAddressData.Length > 0)
                    {
                        for (int i = 0; i < rgtAddressData.Length; i++)
                        {
                            Data[base.NodeDataStartNum + i] = rgtAddressData[i];
                        }
                    }
                    //添加数据
                    if (NodeData != null && NodeData.Length > 0)
                    {
                        for (int i = 0; i < NodeData.Length; i++)
                        {
                            Data[base.NodeDataStartNum + nLengthtAddress + i] = NodeData[i];
                        }
                    }
                    //截取新数组[去除后两位校验字节]
                    byte[] Bytes = new byte[Data.Length - 2];
                    Array.ConstrainedCopy(Data, 0, Bytes, 0, (Data.Length - 2));
                    switch (argNodeItem.CheckType)
                    {
                        case DeviceCheck.CRC:
                            //计算CRC和包尾
                            byte[] CRCBytes = Bytes.ToCheckCRCModbus();
                            //赋值
                            Data[Data.Length - 2] = CRCBytes[0];
                            Data[Data.Length - 1] = CRCBytes[1];
                            break;
                    }
                    ResultData.ResultCode = ResultStatus.Succeed;
                    ResultData.Data = Data;
                }
                catch (Exception ep)
                {
                    ResultData.ResultCode = ResultStatus.Exception;
                    ResultData.ResultMsg = "[" + base.DevicePara.DeviceCaption + "][解析Modbus指令数据]->异常：" + ep.Message;
                }
                return ResultData;
            }
        }

        /// <summary>
        /// 解析接收字节数据
        /// </summary>
        /// <param name="argBytes"></param>
        /// <returns></returns>
        protected override IDeviceData<IDevice<IDevicePara>>? ParseDeviceData(byte[] argBytes)
        {
            string strBytes = string.Join(" ", Array.ConvertAll(argBytes, x => x.ToString("X2")));
            var resultData = ParseDeviceData<StructInputRegister>(argBytes);
            if (!resultData.ResultState || resultData.Data == null) return null;

            resultData.Data.RegisterAddress = resultData.Data.SlaveData.RegisterAddress;
            ModbusSlaveData<StructInputShort> modbusSlaveData = new ModbusSlaveData<StructInputShort>(resultData.Data, new StructInputShort(resultData.Data.SlaveData.RegisterData));
            return new DeviceDataModbus<IDevice<IDevicePara>>((IDevice<IDevicePara>)this, strBytes, modbusSlaveData);
        }

        /// <summary>
        /// 解析接收字节数据
        /// </summary>
        /// <typeparam name="TOutput"></typeparam>
        /// <param name="argBytes"></param>
        /// <returns></returns>
        protected ResultModel<ModbusSlaveData<TOutput>> ParseDeviceData<TOutput>(byte[] argBytes)
            where TOutput : struct
        {
            ResultModel<ModbusSlaveData<TOutput>> resultData = new ResultModel<ModbusSlaveData<TOutput>>(ResultStatus.Failure) { Data = new ModbusSlaveData<TOutput>() };
            if (argBytes == null || argBytes.Length < this.NodeDataNum)
            {
                resultData.Data.SlaveData = default;
                resultData.ResultMsg = "[" + base.DevicePara.DeviceCaption + "][读取Modbus串口数据]->读取数据失败";
                return resultData;
            }

            int nLength = argBytes.Length;
            byte ReslutCheckSum = argBytes[nLength - 2];
            byte ReslutStopBit = argBytes[nLength - 1];
            //地址和功能码校验
            //截取新数组[去除后两位校验字节]
            byte[] Bytes = new byte[nLength - 2];
            Array.ConstrainedCopy(argBytes, 0, Bytes, 0, (nLength - 2));
            //根据校验类型，判断校验
            byte[] CRCCheckBytes = Bytes.ToCheckCRCModbus();
            if (CRCCheckBytes[0] != ReslutCheckSum || CRCCheckBytes[1] != ReslutStopBit)
            {
                resultData.Data.SlaveData = default;
                resultData.ResultMsg = "[" + this.DevicePara.DeviceCaption + "][读取Modbus串口数据]->数据校验失败";
                return resultData;
            }

            //起始或头
            resultData.Data.SlaveAddress = argBytes[0];
            resultData.Data.FunctionCode = argBytes[1];
            //指令数据集
            byte[] NodeData = argBytes.Skip(this.NodeDataStartNum).Take((nLength - this.NodeDataNum)).ToArray();
            resultData.Data.SlaveData = NodeData.BytesToStructEndian<TOutput>();
            resultData.ResultCode = ResultStatus.Succeed;
            resultData.ResultMsg = "[" + this.DevicePara.DeviceCaption + "][读取Modbus串口数据]->读取数据成功";
            return resultData;
        }
    }
}
